interface IList {
    id: number
    content: string
    state: number
}

interface IResponse {
    data: IList[]
    msg: string
}

interface IDebounceFn {
    (e: Event): void
}

interface IThrottleFn {
    (e: Event, id: number): void
}


type TInputBlur = {
    targetEle: HTMLElement, 
    childEle: HTMLInputElement, 
    id: number
}

const mainContent: Element = document.getElementsByClassName('mainContent')[0] //主题内容部分
const navRadio: HTMLElement = document.getElementsByClassName('radio')[0] as HTMLElement //获取单选框导航层
const inputContent: HTMLInputElement = document.getElementsByClassName('inputContent')[0] as HTMLInputElement // input输入框
const inputModify: HTMLInputElement = document.createElement('input') //修改时的input输入框
const radioAll: HTMLInputElement = document.getElementsByClassName('all')[0] as HTMLInputElement // '所有' 的单选框
let status1: string = 'all' // 默认为all，导航栏的

// 封装fetch
const fetchData = async (url: string, method: string, data?: string): Promise<any> => {
    if (method.toUpperCase() === 'GET') {
        try {
            let response: Response = await fetch(url)
            return await response.json()
        } catch (err) {
            throw new Error(err)
        }
    }
    if (method.toUpperCase() === 'POST') {
        try {
            let response: Response = await fetch(url, {
                method,
                headers: {
                    'content-type': 'application/json; charset=utf-8'
                },
                body: data
            })
            return await response.json()
        } catch (err) {
            throw new Error(err)
        }
    }
}

// 防抖
const debounce = (fn: IDebounceFn, wait: number): IDebounceFn => {
    let timer: number | null = null
    return (e: Event) => {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn(e)
        }, wait)
    }
}

// 节流
const throttle = (fn: IThrottleFn, wait: number): IThrottleFn => {
    let canRun: boolean = true
    return (e: Event, id: number) => {
        if (!canRun) return
        canRun = false
        setTimeout(() => {
            fn(e, id)
            canRun = true
        }, wait)
    }
}

// 创建主体部分节点
const createDOM = (list: IList[]): void => {
    let information: string
    let moreInformation: string = ''
    list.forEach((item, i) => {
        information = `<div class="information">
                            <div class="boxLabel"><input type="checkbox" name="hobby" ${item.state === 1 ? "checked" : ""} onclick="modifyStatus(${item.id})"><span>${item.content}</span></div>
                            <p>
                                <button class="modify" onclick="modify(event, ${item.id})">修改</button>
                                <button class="del" onclick="del(${item.id})">删除</button>
                            </p>
                        </div>`
        moreInformation = moreInformation.concat(information)
    })
    mainContent.innerHTML = moreInformation
}

// 修改选中状态
const modifyStatus = throttle((e: Event, id: Number): void => {
    fetchData('/todo/change', 'post', JSON.stringify({ id, nowState: status1 })).then((res: IResponse) => {
        createDOM(res.data)
    })
}, 1000)

// 删除元素
const del = throttle((e: Event, id: number): void => {
    fetchData('/todo/del', 'post', JSON.stringify({
        id,
        nowState: status1
    })).then((res: IResponse) => {
        createDOM(res.data)
    })
}, 1000)

// 修改接口请求
const modifyData = (id: number, newContent: string): void => {
    fetchData('/todo/edit', 'post', JSON.stringify({
        nowState: status1,
        newContent,
        id
    })).then(res => {
        createDOM(res.data)
    })
}

// 修改元素
const modify = throttle((e: Event, id: number): void => {
    let boxLabel: HTMLElement = (e.target as HTMLElement).parentNode.previousSibling.previousSibling as HTMLElement
    for (let i = 0; i < boxLabel.childNodes.length; i++) {
        if (boxLabel.childNodes[i].nodeName === 'SPAN') {
            inputModify.value = (boxLabel.childNodes[i] as HTMLElement).innerHTML
            boxLabel.removeChild(boxLabel.childNodes[i])
        }
    }
    if (boxLabel.childNodes.length === 1) { //当div下只有一个多选框时才将text框加进去，不然会出现多次添加input的问题
        boxLabel.appendChild(inputModify)
    }
    inputModify.onblur = inputBlur({targetEle: boxLabel, childEle: inputModify, id})
}, 1000)


// 修改输入框失去焦点事件
const inputBlur = (params: TInputBlur): (e: Event) => void => {
    const {targetEle, childEle, id} = params
    return (e: Event) => {
        targetEle.removeChild(childEle)
        modifyData(id, (e.target as HTMLInputElement).value)
    }
}

// 输入内容加到主体内容里
const addToMain = (e: Event): void => {
    let value: string = (e.target as HTMLInputElement).value
    // 当输入框里有值的时候在请求接口
    if (value) {
        fetchData('/todo/add', 'post', JSON.stringify({
            content: value
        })).then((res: IResponse) => {
            createDOM(res.data)
        })
    }
    (e.target as HTMLInputElement).value = ''
    radioAll.checked = true
}

// 点击切换单选框的选中状态
const clickRadio = (e: Event): void => {
    if ((e.target as HTMLInputElement).nodeName === 'INPUT') {
        status1 = (e.target as HTMLInputElement).value
    }
    fetchData('/todo/getList?finishState=' + status1, 'get').then((res: IResponse) => {
        createDOM(res.data)
    })
}

// 页面刚进入时调用获取数据接口，渲染页面
const loadPage = (): void => {
    fetchData('/todo/getList?finishState=all', 'get').then((res: IResponse) => {
        createDOM(res.data)
    })
}
loadPage()
// 输入框失去焦点时将内容加到主体里
inputContent.onblur = debounce(addToMain, 1000)

// Enter键按下事件
inputContent.onkeydown = debounce((e: Event) => {
    if ((e as KeyboardEvent).key === 'enter') { // enter对应13
        addToMain(e)
    }
}, 1000)

// 点击上方导航栏触发的页切事件
navRadio.onclick = debounce(clickRadio, 1000)


